package com.kang.c0c.tree;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;

import com.kang.c0c.symbol.FunctionSymbol;
import com.kang.c0c.symbol.Symbol;
import com.kang.c0c.symbol.GlobalTable;
import com.kang.c0c.tree.node.BinaryNode;
import com.kang.c0c.tree.node.FunctionNode;
import com.kang.c0c.tree.node.LeafNode;
import com.kang.c0c.tree.node.Node;
import com.kang.c0c.tree.node.Nodes;
import com.kang.c0c.tree.node.SimpleBlock;
import com.kang.c0c.util.Context;

public class TreeScanner {
	private Tree tree;
	private PrintWriter out;
	private int lineNo;
	private int labelNo;
	private GlobalTable globalTable;
	
	private Hashtable<Node,String> tempVar;
	
	public TreeScanner(Context context, String fileName){
		tree = Tree.instance(context);
		globalTable = GlobalTable.instance(context);
		lineNo = 0;
		labelNo = -1;
		
		tempVar = new Hashtable<Node,String>();
		
		out = null;
		try{
			File file = new File("src/com/kang/c0c/out/" + fileName);
			
			if(file.exists() && !file.isDirectory()){
				out = new PrintWriter(file);
			}else if(! file.exists()){
				if(!file.createNewFile())
					System.err.println("Cann't create the new file " + file.getAbsolutePath());
				else
					out = new PrintWriter(file);
			}else{
				System.err.println("The output file is a directory!");
			}
		}catch(IOException ex){
			ex.printStackTrace();
		}
		
		if(out == null)
			out = new PrintWriter(System.out);		
	}
	
	private void print(String op, String arg1){
		print(op,arg1,null,null);
	}
	
	private void print(String op, Node arg1){
		print(op, nodeString(arg1), null, null);
	}
	
	private void print(String op, String arg1, String result){
		print(op,arg1,null,result);
	}
	
	private void print(String op, Node arg1, Node result){
		print(op, nodeString(arg1),nodeString(result));
	}
	
	private void print(String op, String arg1, String arg2, String result){
		if(arg1 == null)
			arg1 = "";
		if(arg2 == null)
			arg2 = "";
		if(result == null)
			result = "";
		
		out.printf("%4s:%12s%12s%12s%12s\n", "L"+lineNo, op, arg1, arg2, result);
		lineNo ++;
	}
	
	private void print(String op, Node arg1, Node arg2, Node result){
		print(op,nodeString(arg1),nodeString(arg2),nodeString(result));
	}
	
	private String nodeString(Node node){
		if(node == null)
			return "";
		
		if(node instanceof LeafNode){
			LeafNode leaf = (LeafNode)node;
			switch(leaf.getNode()){
			case Nodes.NUM:
				return "" + leaf.getValue();
			case Nodes.VAR:
				Symbol sym = (Symbol)leaf.getValue();
				return sym.getName();
			case Nodes.STRING:
				return globalTable.getString((Integer)leaf.getValue());
			default:
				return "";	
			}
		}else{
			return lookfor(node);
		}		
	}
	
	private String lookfor(Node node){
		String temp = tempVar.get(node);
		
		if(temp == null){
			temp = "$t" + tempVar.size();
			tempVar.put(node, temp);
		}
		
		return temp;
	}
	
	private String getLabel(){
		labelNo ++;
		return "$Label" + labelNo;
	}
		
	private void printIfRelation(Node cond, String label){		
		if(cond == null)
			return;
		if(cond instanceof LeafNode)
			return;
		
		String op = "";
		BinaryNode condB = (BinaryNode)cond;
		
		switch(cond.getNode()){
		case Nodes.EQU:
			op = "JE";
			break;
		case Nodes.NEQ:
			op = "JNE";
			break;
		case Nodes.GT:
			op = "JA";
			break;
		case Nodes.GEQ:
			op = "JAE";
			break;
		case Nodes.LT:
			op = "JL";
			break;
		case Nodes.LEQ:
			op = "JLE";
			break;
		default:
			break;
		}
		
		print(op, nodeString(condB.getLeft()),
				nodeString(condB.getRight()), label);
	}
	
	private void printWhileRelation(Node cond, String label){		
		if(cond == null)
			return;
		if(cond instanceof LeafNode)
			return;
		
		String op = "";
		BinaryNode condB = (BinaryNode)cond;
		
		switch(cond.getNode()){
		case Nodes.EQU:
			op = "JNE";
			break;
		case Nodes.NEQ:
			op = "JE";
			break;
		case Nodes.GT:
			op = "JLE";
			break;
		case Nodes.GEQ:
			op = "JL";
			break;
		case Nodes.LT:
			op = "JAE";
			break;
		case Nodes.LEQ:
			op = "JA";
			break;
		default:
			break;
		}
		
		print(op, nodeString(condB.getLeft()),
				nodeString(condB.getRight()), label);
	}
	
	private void showLoopNode(SimpleBlock simple){
		String start = getLabel();
		String end = getLabel();
		print("STAMP",start);
		
		BinaryNode loop = simple.getBinary(0);
		BinaryNode cond = simple.getBinary(simple.binarySize()-1);
		simple.removeLast();		
		simple.removeFirst();
		
		if(simple.binarySize() > 0)
			showBlock(simple);
		
		printWhileRelation(cond,end);
		
		SimpleBlock execBlock = (SimpleBlock)loop.getRight();
		showBlock(execBlock);
		print("GOTO",start);
		print("STAMP",end);
	}
	
	private void showBlock(SimpleBlock simple){
		if(simple == null)
			return;
		
		int i = 0, length = simple.binarySize();
		
		if(simple.getBinary(0).getNode() == Nodes.WHILE)
			showLoopNode(simple);
		else{
			while(i < length){
				BinaryNode binary = simple.getBinary(i);
				switch(binary.getNode()){
				//算术运算符
				case Nodes.PLU:
					print("ADD",binary.getLeft(),binary.getRight(),binary);
					break;
				case Nodes.SUB:
					print("SUB",binary.getLeft(),binary.getRight(),binary);
					break;
				case Nodes.MUL:
					print("MUL",binary.getLeft(),binary.getRight(),binary);
					break;
				case Nodes.DIV:
					print("DIV",binary.getLeft(),binary.getRight(),binary);
					break;
					
				//赋值
				case Nodes.EVA:
					print("MOV",binary.getRight(),binary.getLeft());
					break;
				
				//函数调用及其参数列表
				case Nodes.PARAM:
					print("PARAM",binary.getLeft());
					break;
				case Nodes.CALL:
					LeafNode leaf = (LeafNode)binary.getLeft();
					FunctionSymbol sym = (FunctionSymbol)leaf.getValue();
					
					if(FunctionSymbol.isInt(sym))
						print("CALL",sym.getName(),nodeString(binary));
					else
						print("CALL",sym.getName());
					break;
				
				//系统函数
				case Nodes.RETURN:
					print("RET",binary.getLeft());
					break;
				case Nodes.SCAN:
					print("SCAN",binary.getLeft());
					break;
				case Nodes.PRINT:
					print("PRINT",binary.getLeft(),binary.getRight(),null);
					break;
					
				//控制流
				case Nodes.IF:
					Node cond = binary.getLeft();
					String label1 = getLabel();
					printIfRelation(cond,label1);
					
					BinaryNode node = (BinaryNode)binary.getRight();
					showBlock((SimpleBlock)node.getRight());
					String label2 = getLabel();
					print("GOTO",label2);
					
					print("STAMP",label1);
					showBlock((SimpleBlock)node.getLeft());
					print("STAMP",label2);
					
					break;				
				default:
					break;				
				}
				
				i ++;
			}
		}
		
		if(simple.next != null)
			showBlock(simple.next);
	}
	
	public void showTree(){
		out.println("Middle Code>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
		out.println();
		
		out.printf("%4s:%12s%12s%12s%12s\n", "Line" , "operator", "arg1", "arg2", "result");
		out.println("-----------------------------------------------------");
		
		int i = 0;
		int treeSize = tree.size();
		
		while(i <treeSize){
			FunctionNode func = tree.getByName(tree.getFunctionName(i));
			print("Function",func.getSym().getName());
			
			if(func.getBlock().size() > 0)
				showBlock((SimpleBlock)func.getBlock().get(0));
			print("EndFunc","");

			out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
			
			i++;
		}
		
		out.close();
	}
}